﻿using Microsoft.AspNet.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Core.Dtos;
using Core.IServices;
using Core.Domain;
using Core.Mappers;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Diagnostics;
using Library.Logging;
using AutoMapper;

namespace Services
{    
    /*http://dotnet.dzone.com/articles/simple-implementation*/
    public class UserStoreService : IUserStoreService, IUserStore<UserDto, string>, IUserPasswordStore<UserDto, string>, IUserSecurityStampStore<UserDto>, IUserRoleStore<UserDto, string>, IUserLoginStore<UserDto>
    {        
        private readonly UserStore<AppUser, Role, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim> userStore;   

        public UserStoreService(UserStore<AppUser, Role, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim> userStore)
        {        
            this.userStore = userStore;    
        }

        public async Task CreateAsync(UserDto user)
        {
            await CreateAndGetIdAsync(user);
        }
        
        public async Task<string> CreateAndGetIdAsync(UserDto user)
        {
            var appUser = user.Map<AppUser>();
            await userStore.CreateAsync(appUser);
            user.Id = appUser.Id;
            return user.Id;
        }

        public async Task DeleteAsync(UserDto user)
        {
            var appUser = await userStore.FindByIdAsync(user.Id);
            await userStore.DeleteAsync(appUser);
        }

        public async Task<UserDto> FindByIdAsync(string userId)
        {
            AppUser appUser = await userStore.FindByIdAsync(userId);
            return appUser.Map<UserDto>();
        }

        public async Task<UserDto> FindByNameAsync(string userName)        
        {
            AppUser appUser = await userStore.FindByNameAsync(userName);
            return appUser.Map<UserDto>();
        }

        public async Task UpdateAsync(UserDto user)
        {
            var appUser = await userStore.FindByIdAsync(user.Id);
            Mapper.Map<UserDto, AppUser>(user, appUser);
            await userStore.UpdateAsync(appUser);
        }

        public void Dispose()
        {
            userStore.Dispose();
        }

        public async Task<string> GetPasswordHashAsync(UserDto user)
        {
            var appUser = await userStore.FindByIdAsync(user.Id);                                     
            return await userStore.GetPasswordHashAsync(appUser);
        }

        public async Task<bool> HasPasswordAsync(UserDto user)
        {
            var appUser = await userStore.FindByIdAsync(user.Id);
            return await userStore.HasPasswordAsync(appUser);
        }

        public async Task SetPasswordHashAsync(UserDto user, string passwordHash)
        {            
            var appUser = user.Map<AppUser>();
            await userStore.SetPasswordHashAsync(appUser, passwordHash);
            user.PasswordHash = passwordHash;
        }

        public async Task<string> GetSecurityStampAsync(UserDto user)
        {
            var appUser = await userStore.FindByIdAsync(user.Id);
            return await userStore.GetSecurityStampAsync(appUser);
        }

        public async Task SetSecurityStampAsync(UserDto user, string stamp)
        {            
            var appUser = user.Map<AppUser>();
            await userStore.SetSecurityStampAsync(appUser, stamp);
            user.SecurityStamp = stamp;
        }

        public async Task AddToRoleAsync(UserDto user, string roleName)
        {
            var appUser = await userStore.FindByIdAsync(user.Id);
            await userStore.AddToRoleAsync(appUser, roleName);
        }

        public async Task<IList<string>> GetRolesAsync(UserDto user)
        {
            var appUser = await userStore.FindByIdAsync(user.Id);
            var result = await userStore.GetRolesAsync(appUser);
            return result;
        }

        public async Task<bool> IsInRoleAsync(UserDto user, string roleName)
        {
            var appUser = await userStore.FindByIdAsync(user.Id);
            return await userStore.IsInRoleAsync(appUser, roleName);
        }

        public async Task RemoveFromRoleAsync(UserDto user, string roleName)
        {
            var appUser = await userStore.FindByIdAsync(user.Id);
            await userStore.RemoveFromRoleAsync(appUser, roleName);
        }

        //direct use
        public async Task AddLoginAsync(UserDto user, UserLoginInfo login)
        {
            var appUser = await userStore.FindByIdAsync(user.Id);
            await userStore.AddLoginAsync(appUser, login);
        }

        public async Task<UserDto> FindAsync(UserLoginInfo login)
        {            
            var appUser = await userStore.FindAsync(login);
            return appUser.Map<UserDto>();
        }

        public async Task<IList<UserLoginInfo>> GetLoginsAsync(UserDto user)
        {
            var appUser = await userStore.FindByIdAsync(user.Id);
            return await userStore.GetLoginsAsync(appUser);
        }

        public async Task RemoveLoginAsync(UserDto user, UserLoginInfo login)
        {
            var appUser = await userStore.FindByIdAsync(user.Id);
            await userStore.RemoveLoginAsync(appUser, login);
        }

        //proxy use
        public async Task AddLoginAsync(UserDto user, UserLoginInfoTo loginTo)
        {
            UserLoginInfo login = loginTo.Map<UserLoginInfo>();
            await AddLoginAsync(user, login);
        }

        public async Task<UserDto> FindAsync(UserLoginInfoTo loginTo)
        {
            UserLoginInfo login = loginTo.Map<UserLoginInfo>();
            return await FindAsync(login);
        }

        public async Task RemoveLoginAsync(UserDto user, UserLoginInfoTo loginTo)
        {
            UserLoginInfo login = loginTo.Map<UserLoginInfo>();
            await RemoveLoginAsync(user, loginTo);
        }

        public async Task<IList<UserLoginInfoTo>> GetLoginsAsyncAsTos(UserDto user)
        {
            var list = await GetLoginsAsync(user);
            return list.Map<UserLoginInfo, UserLoginInfoTo>();
        }
    }
}